﻿class Game
{
    constructor() { }

    app: PIXI.Application;
    screenScale: number;
    uiContainer: PIXI.Container;
    puzzleContainer: PIXI.Container;
    texturesMain: PIXI.Texture[];
    mapDom: HTMLElement;
    level: number;
    textureSize: number = 512;
    tileScale: number;
    blockSize: number;
    puzzleSize: number;
    puzzleScale: number = 0.9;
    curBlock: string;
    answer: { [key: string]: PIXI.Point };
    isFinish: boolean;

    start()
    {
        var width = window.innerWidth;
        var height = window.innerHeight;
        if (height / width < 1.4)
        {
            width = Math.round(height / 1.4);
        }

        this.app = new PIXI.Application(innerWidth = width, innerHeight = height, { backgroundColor: 0xffffff });
        document.body.appendChild(this.app.view);
        this.app.view.style.position = 'absolute';
        this.app.view.style.left = ((document.body.clientWidth - width) / 2) + 'px';
        this.screenScale = 1;
        this.app.stage.scale.set(this.screenScale, this.screenScale);

        this.startGame(1);
    }

    startGame(level: number)
    {
        this.app.stage.removeChild(this.uiContainer);
        this.app.stage.removeChild(this.puzzleContainer);

        this.uiContainer = new PIXI.Container;
        this.app.stage.addChild(this.uiContainer);
        this.puzzleContainer = new PIXI.Container();
        this.app.stage.addChild(this.puzzleContainer);

        this.isFinish = false;
        this.tileScale = this.app.screen.width * this.puzzleScale / this.textureSize;
        this.setLevel(level);
        this.loadTextures();
        this.initPuzzle();
        this.initUI();
    }

    setLevel(level: number)
    {
        this.level = level;
        this.puzzleSize = this.level + 2;
        this.blockSize = this.app.screen.width * this.puzzleScale / (this.level + 2);
    }

    loadTextures()
    {
        this.texturesMain = [];
        this.texturesMain.push(null);

        var str = window.location.hash.substr(1);
        if (str.length > 0)
        {
            for (let i = 1; i <= 4; i++)
            {
                let texture = PIXI.Texture.fromImage('img/' + str + '/level' + i + '.jpg');
                this.texturesMain.push(texture);
            }
        }
        else
        {
            for (let i = 1; i <= 4; i++)
            {
                let texture = PIXI.Texture.fromImage('img/level' + i + '.jpg');
                this.texturesMain.push(texture);
            }
        }
    }

    initPuzzle()
    {
        let offsetX = this.app.screen.width * (1 - this.puzzleScale) / 2;
        let offsetY = this.app.screen.height - this.puzzleSize * this.blockSize - 24;

        let refSprite = new PIXI.Sprite(this.texturesMain[this.level]);
        refSprite.width = Math.min(offsetY - 48, this.app.screen.width - offsetX * 3 - 256);
        refSprite.height = refSprite.width;
        refSprite.position.set(offsetX, 24);
        this.puzzleContainer.addChild(refSprite);
        // refSprite.interactive = true;
        // refSprite.on('pointerdown', (e) =>
        // {
        //     window.location.reload(true);
        // });

        this.curBlock = (this.puzzleSize - 1) + '_' + (this.puzzleSize - 1);
        this.answer = {};
        let sprite : PIXI.extras.TilingSprite;
        for (let i = 0; i < this.puzzleSize; i++)
        {
            for (let j = 0; j < this.puzzleSize; j++)
            {
                sprite = new PIXI.extras.TilingSprite(this.texturesMain[this.level]);
                sprite.name = i + '_' + j;
                sprite.tilePosition.set(-this.blockSize * j, -this.blockSize * i);
                sprite.tileScale.set(this.tileScale, this.tileScale);
                sprite.position.set(offsetX + this.blockSize * j, offsetY + this.blockSize * i);
                sprite.width = this.blockSize - 1;
                sprite.height = this.blockSize - 1;
                this.puzzleContainer.addChild(sprite);
                sprite.interactive = true;
                sprite.on('pointerdown', (e) => { this.spriteClicked(e.currentTarget); });

                if (i == this.puzzleSize - 1 && j == this.puzzleSize - 1)
                {
                    sprite.alpha = 0;
                }

                let pos = new PIXI.Point(sprite.tilePosition.x, sprite.tilePosition.y);
                this.answer[sprite.name] = pos;
            }
        }

        this.randomPuzzle();
    }

    randomPuzzle()
    {
        let x = this.puzzleSize - 1;
        let y = this.puzzleSize - 1;
        let randomCount = this.puzzleSize * this.puzzleSize * 100;
        while (--randomCount > 0)
        {
            let randX = 0;
            let randY = 0;
            let rand = Math.floor(Math.random() * 4);
            switch (rand) {
                case 0:
                    randX = 1;
                    break;
                case 1:
                    randY = 1;
                    break;
                case 2:
                    randX = -1;
                    break;
                case 3:
                    randY = -1;
                default:
                    break;
            }
            if (x + randX >= 0 && x + randX < this.puzzleSize && y + randY >=0 && y + randY < this.puzzleSize)
            {
                let a = x + '_' + y;
                x += randX;
                y += randY;
                let b = x + '_' + y;

                let spriteA = this.puzzleContainer.getChildByName(a) as PIXI.extras.TilingSprite;
                let spriteB = this.puzzleContainer.getChildByName(b) as PIXI.extras.TilingSprite;

                this.switchSprite(spriteA, spriteB);
            }
        }
        for (let i = x; i < this.puzzleSize - 1; i++)
        {
            let a = x + '_' + y;
            x++;
            let b = x + '_' + y;

            let spriteA = this.puzzleContainer.getChildByName(a) as PIXI.extras.TilingSprite;
            let spriteB = this.puzzleContainer.getChildByName(b) as PIXI.extras.TilingSprite;

            this.switchSprite(spriteA, spriteB);
        }
        for (let i = y; i < this.puzzleSize - 1; i++)
        {
            let a = x + '_' + y;
            y++;
            let b = x + '_' + y;

            let spriteA = this.puzzleContainer.getChildByName(a) as PIXI.extras.TilingSprite;
            let spriteB = this.puzzleContainer.getChildByName(b) as PIXI.extras.TilingSprite;

            this.switchSprite(spriteA, spriteB);
        }
    }

    switchSprite(spriteA: PIXI.extras.TilingSprite, spriteB: PIXI.extras.TilingSprite)
    {
        let posX = spriteA.tilePosition.x;
        let posY = spriteA.tilePosition.y;
        let alpha = spriteA.alpha;
        spriteA.tilePosition.copy(spriteB.tilePosition);
        spriteA.alpha = spriteB.alpha;
        spriteB.tilePosition.x = posX;
        spriteB.tilePosition.y = posY;
        spriteB.alpha = alpha;
    }

    initUI()
    {
        let sprite: PIXI.Sprite;
        for (let i = 1; i <= 4; i++)
        {
            sprite = new PIXI.Sprite(PIXI.Texture.fromImage('img/button' + i + '.jpg'));
            sprite.name = i.toString();
            sprite.anchor.set(1, 0);
            sprite.position.set(this.app.screen.width - this.app.screen.width * (1 - this.puzzleScale) / 2, 24 + (i - 1) * 76);
            this.uiContainer.addChild(sprite);
            sprite.interactive = true;
            sprite.on('pointerdown', (e) => { (e.currentTarget as PIXI.Sprite).alpha = 0.7; });
            sprite.on('pointerupoutside', (e) => { (e.currentTarget as PIXI.Sprite).alpha = 1; });
            sprite.on('pointerup', (e) =>
            {
                (e.currentTarget as PIXI.Sprite).alpha = 1;
                
                let level = parseInt(e.currentTarget.name);
                this.startGame(level);
            });
        }
    }

    spriteClicked(object : PIXI.DisplayObject)
    {
        if (this.isFinish)
        {
            return;
        }

        let sprite = object as PIXI.extras.TilingSprite;
        if (this.canMove(sprite.name, this.curBlock))
        {
            let curSprite = this.puzzleContainer.getChildByName(this.curBlock) as PIXI.extras.TilingSprite;
            let posX = sprite.tilePosition.x;
            let posY = sprite.tilePosition.y;
            sprite.tilePosition.copy(curSprite.tilePosition);
            sprite.alpha = 0;
            curSprite.tilePosition.x = posX;
            curSprite.tilePosition.y = posY;
            curSprite.alpha = 1;

            this.curBlock = sprite.name;

            if (this.checkFinish())
            {
                sprite.alpha = 1;
                this.isFinish = true;

                this.clearMargin();
            }
        }
    }

    canMove(blockA: string, blockB: string): boolean
    {
        let aX = parseInt(blockA.split('_')[0]);
        let aY = parseInt(blockA.split('_')[1]);
        let bX = parseInt(blockB.split('_')[0]);
        let bY = parseInt(blockB.split('_')[1]);
        return Math.abs(aX - bX) + Math.abs(aY - bY) == 1;
    }

    checkFinish(): boolean
    {
        for (let i = 0; i < this.puzzleSize; i++)
        {
            for (let j = 0; j < this.puzzleSize; j++)
            {
                let name = i + '_' + j;
                let sprite = this.puzzleContainer.getChildByName(name) as PIXI.extras.TilingSprite;
                let pos = this.answer[name];
                if (pos.x != sprite.tilePosition.x || pos.y != sprite.tilePosition.y)
                {
                    return false;
                }
            }
        }
        return true;
    }

    clearMargin()
    {
        for (let i = 0; i < this.puzzleSize; i++)
        {
            for (let j = 0; j < this.puzzleSize; j++)
            {
                let name = i + '_' + j;
                let sprite = this.puzzleContainer.getChildByName(name) as PIXI.extras.TilingSprite;
                sprite.width = this.blockSize;
                sprite.height = this.blockSize;
            }
        }
    }
}

window.onload = () =>
{
    var game = new Game();
    game.start();
};
